package com.lagou.SqlSessionFactory;

import com.lagou.pojo.Configuration;
import com.lagou.pojo.MappedStatement;
import com.lagou.utils.GenericTokenParser;
import com.lagou.utils.ParameterMapping;
import com.lagou.utils.ParameterMappingTokenHandler;
import jdk.nashorn.internal.ir.CallNode;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * <pre>
 * 描述：TODO
 * </pre>
 *
 * @类名：com.lagou.SqlSessionFactory.SimpleExecuter
 * @作者 mengwei
 * 创建日期：2020/9/28 13:33
 */
public class SimpleExecuter implements Executer {
    @Override
    public <T> List<T> query(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws Exception{
//        注册驱动
        Connection connection = configuration.getDateSource().getConnection();
//        获得sql
        String sql = mappedStatement.getSql();
//        解析sql
       BoundSql boundSql= getBoundSql(sql);

        String paramterType = mappedStatement.getParamterType();
       Class<?> paramterTypeClass =  getTypeClass(paramterType);
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSql());
        for (int i = 0; i < boundSql.getParameterMappings().size(); i++) {
            Object param = params[0];
            ParameterMapping parameterMapping = boundSql.getParameterMappings().get(i);


//            使用反射获得参数对应字段
            Field declaredField = paramterTypeClass.getDeclaredField(parameterMapping.getContent());
//            暴力破解
            declaredField.setAccessible(true);
//            开始设置参数
//              todo 注意点：list角标从0开始，但是preparedStatement角标从1开始，所以需要使list角标 加一
            preparedStatement.setObject(i+1,declaredField.get(param));
        }

//        执行sql
        preparedStatement.execute();
        ResultSet resultSet = preparedStatement.getResultSet();
        ArrayList<Object> results = new ArrayList<>();
        String resultType = mappedStatement.getResultType();

//        封装返回结果集
        while (resultSet.next()){
            Class<?> typeClass = getTypeClass(resultType);
            Object o = typeClass.newInstance();

//            获取源
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {

                Object value = resultSet.getObject(metaData.getColumnName(i));
//                todo 使用内省进行赋值
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(metaData.getColumnName(i), typeClass);
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(o,value);

            }

            results.add(o);
        }

        return (List<T>) results;
    }


    private <T> List<T> execute(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws Exception {
        //        注册驱动
        Connection connection = configuration.getDateSource().getConnection();
//        获得sql
        String sql = mappedStatement.getSql();
//        解析sql
        BoundSql boundSql= getBoundSql(sql);

        String paramterType = mappedStatement.getParamterType();
        Class<?> paramterTypeClass =  getTypeClass(paramterType);
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSql());
        for (int i = 0; i < boundSql.getParameterMappings().size(); i++) {
            Object param = params[0];
            ParameterMapping parameterMapping = boundSql.getParameterMappings().get(i);


//            使用反射获得参数对应字段
            Field declaredField = paramterTypeClass.getDeclaredField(parameterMapping.getContent());
//            暴力破解
            declaredField.setAccessible(true);
//            开始设置参数
//              todo 注意点：list角标从0开始，但是preparedStatement角标从1开始，所以需要使list角标 加一
            preparedStatement.setObject(i+1,declaredField.get(param));
        }

//        执行sql


        List result = null;
        switch (mappedStatement.getQualifiedName()){
            case "select": {
                preparedStatement.execute();
                ResultSet resultSet = preparedStatement.getResultSet();
                ArrayList<Object> results = new ArrayList<>();
                String resultType = mappedStatement.getResultType();

//        封装返回结果集
                while (resultSet.next()){
                    Class<?> typeClass = getTypeClass(resultType);
                    Object o = typeClass.newInstance();

//            获取源
                    ResultSetMetaData metaData = resultSet.getMetaData();
                    for (int i = 1; i <= metaData.getColumnCount(); i++) {

                        Object value = resultSet.getObject(metaData.getColumnName(i));
//                todo 使用内省进行赋值
                        PropertyDescriptor propertyDescriptor = new PropertyDescriptor(metaData.getColumnName(i), typeClass);
                        Method writeMethod = propertyDescriptor.getWriteMethod();
                        writeMethod.invoke(o,value);

                    }

                    results.add(o);
                }

                result=results;
                break;

            } case "insert":
            case "update":
            case "delete":{
                int update = preparedStatement.executeUpdate();
                result= new ArrayList<>();
                result.add(update);
                break;
            }
            default: ;
        }
//        return preparedStatement;
        return result;
    }

    @Override
    public int modify(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
        List<Object> execute = execute(configuration, mappedStatement, params);

        int updateCount = (Integer) execute.get(0);

        return updateCount;
    }

    private Class<?> getTypeClass(String paramterType) throws ClassNotFoundException {
        if (null != paramterType) return Class.forName(paramterType);
        return null;
    }

    /**
     * 解析sql，将#｛｝替换为？，并返回BoundSql 对象
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {

        ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", handler);
//        获得解析后的sql
        String parsedSql = genericTokenParser.parse(sql);
//        获得解析后的参数对象
        List<ParameterMapping> parameterMappings = handler.getParameterMappings();

        BoundSql boundSql = new BoundSql(parsedSql, parameterMappings);

        return  boundSql;
    }
}
